package com.wzdigit.wms.raw.service.outstock;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.wzdigit.framework.Shift;
import com.wzdigit.mes.basic.api.BasicParamApi;
import com.wzdigit.mes.basic.dto.ParamDetailDTO;
import com.wzdigit.wms.basic.client.Pager;
import com.wzdigit.wms.basic.client.PagerInfo;
import com.wzdigit.wms.basic.client.request.outstock.CancelPickMaterialRequest;
import com.wzdigit.wms.basic.client.request.outstock.PickMaterialRequest;
import com.wzdigit.wms.basic.client.response.Result;
import com.wzdigit.wms.basic.client.response.basic.SkuBaseResponse;
import com.wzdigit.wms.basic.client.response.basic.UserWhsZoneWorkDivisionResponse;
import com.wzdigit.wms.basic.client.response.outstock.*;
import com.wzdigit.wms.basic.domain.asn.AsnDtl;
import com.wzdigit.wms.basic.domain.asn.AsnHeader;
import com.wzdigit.wms.basic.domain.basic.Location;
import com.wzdigit.wms.basic.domain.basic.Sku;
import com.wzdigit.wms.basic.domain.basic.Warehouse;
import com.wzdigit.wms.basic.domain.basic.WorkDivisionExtSku;
import com.wzdigit.wms.basic.domain.dtos.inventory.GetUserWhsZoneWorkDivisionDto;
import com.wzdigit.wms.basic.domain.dtos.inventory.UpdateInventoryInputDto;
import com.wzdigit.wms.basic.domain.inventory.*;
import com.wzdigit.wms.basic.domain.order.TrsnDtl;
import com.wzdigit.wms.basic.domain.order.TrsnDtlGoods;
import com.wzdigit.wms.basic.domain.order.TrsnHeader;
import com.wzdigit.wms.basic.domain.transfer.GdnDtl;
import com.wzdigit.wms.basic.domain.transfer.GdnDtlGoods;
import com.wzdigit.wms.basic.domain.transfer.GdnHeader;
import com.wzdigit.wms.basic.enums.*;
import com.wzdigit.wms.basic.enums.log.GoodsLogEnum;
import com.wzdigit.wms.basic.enums.operate.OperateCodeEnum;
import com.wzdigit.wms.basic.enums.outstock.RawPickEnum;
import com.wzdigit.wms.basic.mapper.core.basic.*;
import com.wzdigit.wms.basic.mapper.core.inventory.GoodsLogMapper;
import com.wzdigit.wms.basic.mapper.core.inventory.GoodsMapper;
import com.wzdigit.wms.basic.mapper.core.inventory.InvLocMapper;
import com.wzdigit.wms.basic.mapper.core.transfer.GdnDtlGoodsMapper;
import com.wzdigit.wms.basic.mapper.core.transfer.GdnDtlMapper;
import com.wzdigit.wms.basic.mapper.core.transfer.GdnHeaderMapper;
import com.wzdigit.wms.basic.service.SkuPermService;
import com.wzdigit.wms.basic.service.code.GoodsSerialCoreService;
import com.wzdigit.wms.basic.service.code.InventoryService;
import com.wzdigit.wms.basic.service.code.WhsPermService;
import com.wzdigit.wms.basic.util.HttpSessionUtil;
import com.wzdigit.wms.basic.util.enums.SystemModuleEnum;
import com.wzdigit.wms.common.entity.CommonProperty;
import com.wzdigit.wms.common.entity.StatusCode;
import com.wzdigit.wms.raw.client.response.gdn.delivery.DeliveryGoodsResponse;
import com.wzdigit.wms.raw.client.response.gdn.delivery.SimpleGdnDtl;
import com.wzdigit.wms.raw.client.response.gdn.picktask.CancelPickResponse;
import com.wzdigit.wms.raw.client.response.gdn.picktask.PickAllQty;
import com.wzdigit.wms.raw.exception.SystemException;
import com.wzdigit.wms.raw.mapper.gdn.PickTaskMapper;
import com.wzdigit.wms.raw.service.backtosap.WmsBillStatusBackService;
import com.wzdigit.wms.raw.service.gdn.DeliveryNewService;
import com.wzdigit.wms.web.service.wms2sap.wmsbillstatus.ZDT_076_ReqItems;
import lombok.extern.slf4j.Slf4j;
import net.dreamlu.mica.core.utils.BeanUtil;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.poi.ss.formula.functions.T;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * @author AdministratordeliveryNewService
 *
 */
@Service
@Slf4j
public class PickMaterialService {
    private static final List<String> whsGdnType=Arrays.asList("D25","D27");

    @Autowired
    private GdnHeaderMapper gdnHeaderMapper;

    @Autowired
    private PickTaskMapper pickTaskMapper;

    @Autowired
    private GdnDtlMapper gdnDtlMapper;

    @Autowired
    private WarehouseMapper warehouseMapper;

    @Autowired
    private UserWorkDivisonMapper userWorkDivisonMapper;

    @Autowired
    private SkuMapper skuMapper;

    @Autowired
    private GoodsMapper goodsMapper;

    @Autowired
    private InvLocMapper invLocMapper;

    @Autowired
    private WorkDivisionExtSkuMapper workDivisionExtSkuMapper;

    @Autowired
    private GdnDtlGoodsMapper gdnDtlGoodsMapper;

    @Autowired
    private InventoryService inventoryService;

    @Autowired
    private GoodsLogMapper goodsLogMapper;

    @Autowired
    private LocationMapper locationMapper;

    @Autowired
    private WmsBillStatusBackService wmsBillStatusBackService;

    @Autowired
	private SkuPermService skuPermService;

	@Autowired
	private WhsPermService whsCodeService;

    @Autowired
    private RedissonClient redisson;

	@Autowired
    private DeliveryNewService deliveryService;

	@Autowired
	private GoodsSerialCoreService goodsSerialCoreService;

    private Lock lock = new ReentrantLock();

    //TODO ZYF:新加的，给D25/D27手动选择仓库
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void setGdnWhs(Integer gdnHeaderId,Integer whsId,HttpServletRequest servletRequest){
        GdnHeader gdnHeader = gdnHeaderMapper.selectOne(new QueryWrapper<GdnHeader>().eq("GDN_HEADER_ID", gdnHeaderId));
        if (!whsGdnType.contains(gdnHeader.getGdnType())){
            Shift.fatal(gdnHeader.getGdnType()+"出库单无法选择仓库!");
        }
        if (!gdnHeader.getStatus().equals(GdnHeaderStatusEnum.CREATE.code)){
            Shift.fatal("["+gdnHeader.getGdnNo()+"]状态非["+GdnHeaderStatusEnum.CREATE.code+"]，无法选择仓库!");
        }
        List<GdnDtl> dtlList = gdnDtlMapper.selectList(new QueryWrapper<GdnDtl>().eq("GDN_HEADER_ID", gdnHeaderId));
        List<Long> ids = dtlList.stream().map(t -> t.getGdnDtlId()).collect(Collectors.toList());

        //更新仓库
        pickTaskMapper.updateDtlWhs(ids,whsId,HttpSessionUtil.getLoginName(servletRequest),new Date());

//        Shift.fatal("/////////////////");

    }


    /**
     * 拣料通知
     *
     * @param dto
     * @param servletRequest
     * @param servletResponse
     * @return
     */
    public PagerInfo<List<PickNoticeHeaderResponse>> search(PickMaterialRequest dto, Pager<?> pager,
    		HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
        String loginName = HttpSessionUtil.getLoginName(servletRequest);

//        loginName="hxwl001";

        // 当前用户权限的仓库
     	List<Integer> whsIdList = whsCodeService.getWareHouseIds(loginName);

     	PagerInfo<List<PickNoticeHeaderResponse>> pagerInfo = new PagerInfo<>();

		// 原材料拣料仅查看对应的单据类型
		List<String> allowTypeList = GdnTypeEnum.getCodeListBySystemModule(SystemModuleEnum.RAW);
		if (dto.getGdnType() != null) {
			if (!allowTypeList.contains(dto.getGdnType())) {
				pagerInfo.setTotalCount(0);
				pagerInfo.setItems(new ArrayList<>(0));
				return pagerInfo;
			} else {
				allowTypeList = new ArrayList<>(0);
				allowTypeList.add(dto.getGdnType());
			}
		}

		// 单头不允许的状态允许的
		List<Integer> headerNotAllowList = new ArrayList<>(3);
		headerNotAllowList.add(GdnHeaderStatusEnum.CANCEL.getCode());
		headerNotAllowList.add(GdnHeaderStatusEnum.FINISH.getCode());
		headerNotAllowList.add(GdnHeaderStatusEnum.CLOSE.getCode());

		// 明细必须的状态
		List<Integer> dtlMustStatusList = new ArrayList<>(4);
		dtlMustStatusList.add(GdnDtlStatusEnum.CREATE.getCode());
		dtlMustStatusList.add(GdnDtlStatusEnum.HASDONE.getCode());
		dtlMustStatusList.add(GdnDtlStatusEnum.WAITPICK.getCode());
		dtlMustStatusList.add(GdnDtlStatusEnum.PICKING.getCode());
		dtlMustStatusList.add(GdnDtlStatusEnum.WAITLOAD.getCode());

		// 转换发货日期
		if (dto.getShipDateTime() != null) {
			dto.setShipDate(new Date(dto.getShipDateTime()));
		}

		// 物料组权限
		List<Integer> skuIdList = new ArrayList<>(0);
        if (Objects.equals(dto.getIsOwnSelect(), CommonProperty.COMMON_TRUE)) {
        	skuIdList = skuPermService.getSkuIdPerm(loginName, whsIdList);
        }

        Page<T> page = PageHelper.offsetPage(pager.getSkipCount(), pager.getMaxResultCount());
        List<PickNoticeHeaderResponse> items = gdnHeaderMapper.selectOwnOrder(whsIdList, skuIdList, dto, headerNotAllowList, dtlMustStatusList, allowTypeList);
        this.setPickOtherProperties(items);
        pagerInfo.setItems(items);
        pagerInfo.setTotalCount(page.getTotal());
        return pagerInfo;
    }

    /**
     * 设置拣货进度，状态
     *
     * @param pickNoticeHeaderResponseList
     */
    private void setPickOtherProperties(List<PickNoticeHeaderResponse> pickNoticeHeaderResponseList) {
        for (PickNoticeHeaderResponse pickNoticeHeaderResponse : pickNoticeHeaderResponseList) {

            List<GdnDtl> gdnDtls = gdnDtlMapper.selectByHeaderId(pickNoticeHeaderResponse.getGdnHeaderId());

            BigDecimal pickQty = gdnDtls.stream().map(s -> s.getPickQty()).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal orderQty = gdnDtls.stream().map(s -> s.getOrderQty()).reduce(BigDecimal.ZERO, BigDecimal::add);

            //设置状态
            if (pickQty.compareTo(BigDecimal.ZERO) == 0) {
                pickNoticeHeaderResponse.setStatus("待拣料");
            } else if (orderQty.compareTo(pickQty) == 1) {
                pickNoticeHeaderResponse.setStatus("拣料中");
            } else if (orderQty.compareTo(pickQty) == 0) {
                pickNoticeHeaderResponse.setStatus("已拣料");
            }


            Map<Integer, List<GdnDtl>> skuMap = gdnDtls.stream().collect(Collectors.groupingBy(s -> s.getSkuId()));

            //
            int totalSkuSize = skuMap.size();
            int pickSize = 0;

            for (Map.Entry<Integer, List<GdnDtl>> entry : skuMap.entrySet()) {
                List<GdnDtl> dtls = entry.getValue();

                BigDecimal pickQty2 = dtls.stream().map(s -> s.getPickQty()).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal orderQty2 = dtls.stream().map(s -> s.getOrderQty()).reduce(BigDecimal.ZERO, BigDecimal::add);

                if (pickQty2.compareTo(orderQty2) == 0) {
                    pickSize++;
                }
            }

            String pickSchedule = pickSize + "/" + totalSkuSize;
            pickNoticeHeaderResponse.setPickSchedule(pickSchedule);


            if (pickNoticeHeaderResponse.getPriority() == 0) {
                pickNoticeHeaderResponse.setPriorityDesc("普通");
            } else {
                pickNoticeHeaderResponse.setPriorityDesc("紧急");
            }
        }
    }

    /**
     * 拣料单据类型
     *
     * @return
     */
    public Result<List<GdnTypeEnum>> getPickMaterialList() {
        List<String> allowTypeList = GdnTypeEnum.getCodeListBySystemModule(SystemModuleEnum.RAW);
        GdnTypeEnum[] values = GdnTypeEnum.values();
        List<GdnTypeEnum> gdnTypeEnums = Arrays.asList(values);

        gdnTypeEnums = gdnTypeEnums.stream().filter(s -> allowTypeList.contains(s.getCode())).collect(Collectors.toList());

        Result<List<GdnTypeEnum>> result = new Result<>();
        result.setData(gdnTypeEnums);
        return result;
    }

    /**
     * 根据单头找明细物料集合
     *
     * @param gdnHeaderId
     * @param servletRequest
     * @param servletResponse
     */
    public Result<List<PickMaterialSkuResponse>> getSkuByGdnHeaderId(Long gdnHeaderId,Integer isOwnSelect,
    		HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
    	String loginName = HttpSessionUtil.getLoginName(servletRequest);

    	// 当前用户权限的仓库
     	List<Integer> whsIdList = whsCodeService.getWareHouseIds(loginName);

     	// 查询单据明细
     	List<GdnDtl> gdnDtlList = gdnDtlMapper.selectByHeaderId(gdnHeaderId);

		if (Objects.equals(isOwnSelect, CommonProperty.COMMON_TRUE)) {
			// 物料组权限
			List<Integer> skuIdList = skuPermService.getSkuIdPerm(loginName, whsIdList);
			gdnDtlList = gdnDtlList.stream().filter(s -> skuIdList.contains(s.getSkuId())).collect(Collectors.toList());
		}

        // 根据物料分组
        Map<Integer, List<GdnDtl>> gdnDtlMap = gdnDtlList.stream().collect(Collectors.groupingBy(s -> s.getSkuId()));

        List<PickMaterialSkuResponse> list = new ArrayList<>(gdnDtlMap.size());
        for (Map.Entry<Integer, List<GdnDtl>> entry : gdnDtlMap.entrySet()) {
        	Sku sku = skuMapper.selectById(entry.getKey());
            List<GdnDtl> dtlList = entry.getValue();

            BigDecimal pickQty = dtlList.stream().map(s -> s.getPickQty()).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal orderQty = dtlList.stream().map(s -> s.getOrderQty()).reduce(BigDecimal.ZERO, BigDecimal::add);

            PickMaterialSkuResponse item = new PickMaterialSkuResponse();
            item.setPickQty(pickQty);
            item.setOrderQty(orderQty);
            item.setSkuId(sku.getSkuId());
            item.setSkuCode(sku.getSkuCode());
            item.setSkuDesc(sku.getDescCn());
            list.add(item);
        }
        List<PickMaterialSkuResponse> data = list.stream().filter(s -> s.getOrderQty().compareTo(s.getPickQty()) == 1).collect(Collectors.toList());
        list.removeAll(data);
        data.addAll(list);
        return new Result<List<PickMaterialSkuResponse>>(data);
    }

    /**
     * 拣料页面查询信息
     *
     * @param gdnHeaderId
     * @param skuId
     * @param servletRequest
     * @param servletResponse
     * @return
     */
    public Result<PickInfoResponse> getPickInfo(Integer gdnHeaderId, Integer skuId, HttpServletRequest servletRequest, HttpServletResponse servletResponse) {


        PickInfoResponse pickInfoResponse = new PickInfoResponse();

        GdnHeader gdnHeader = gdnHeaderMapper.selectById(gdnHeaderId);
        pickInfoResponse.setSourceNo(gdnHeader.getSourceShipNotice());


        pickInfoResponse.setSkuId(skuId);

        Sku sku = skuMapper.selectById(skuId);
        pickInfoResponse.setSkuCode(sku.getSkuCode());
        pickInfoResponse.setSkuDesc(sku.getDescCn());

        //创建类型的条件构造器
        QueryWrapper<GdnDtl> wrapper = new QueryWrapper<>();
        wrapper.eq("GDN_HEADER_ID", gdnHeaderId);
        wrapper.eq("SKU_ID", skuId);

        List<GdnDtl> gdnDtls = gdnDtlMapper.selectList(wrapper);

        GdnDtl gdnDtl = gdnDtls.get(0);
        pickInfoResponse.setRequireTime(gdnDtl.getRequireTime());
        pickInfoResponse.setWhsId(gdnDtl.getWhsId());

        if (gdnDtl.getWhsId()!=null){
            Warehouse warehouse = warehouseMapper.selectById(gdnDtl.getWhsId());
            pickInfoResponse.setWhsName(warehouse.getWhsName());
        }


        BigDecimal pickQty = gdnDtls.stream().map(s -> s.getPickQty()).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal orderQty = gdnDtls.stream().map(s -> s.getOrderQty()).reduce(BigDecimal.ZERO, BigDecimal::add);

        pickInfoResponse.setPickQty(pickQty);
        pickInfoResponse.setOrderQty(orderQty);

        if (pickQty.compareTo(BigDecimal.ZERO) == 0) {
            pickInfoResponse.setStatus("待拣料");
        } else if (pickQty.compareTo(orderQty) == -1) {
            pickInfoResponse.setStatus("拣料中");
        } else if (pickQty.compareTo(orderQty) == 0) {
            pickInfoResponse.setStatus("已拣料");
        }

        Result<PickInfoResponse> result = new Result<>();
        result.setData(pickInfoResponse);
        return result;
    }

    /**
     * ①校验物料仓库
     * 物料所在仓库必须和出库单发料仓库一致，否则提示：SN[XXX]所属仓库[YYY]和发料仓库[ZZZ]不一致，不能拣料！
     * ②校验物料状态
     * 物料状态(status)必须为2(待上架)、3(已上架)、4(已下架)，否则提示：SN[XXX]状态为[YYY]，不能拣料！
     * ③校验物料品质状态
     * 物料品质状态(qa_status)必须和出库单明细的品质状态(qa_status)一致，否则提示：SN[XXX]品质状态为[YY]，不符合要求，不能拣料！
     * 注：品质状态需转换为中文描述。
     * ④校验物料编码
     * 物料编码必须和当前的要求一致，否则提示：SN[XXX]的物料编码[YYY]与当前拣料需求不一致，不能拣料！
     * ⑤校验工厂代码
     * 工厂代码必须和当前的要求一致，否则提示：SN[XXX]的工厂代码[YYY]与当前拣料需求不一致，不能拣料！
     * ⑥校验数量
     * 扫描的SN的物料数量不能超过剩余需求数量，否则弹出【物料拆分】窗口。
     *
     * @param gdnHeaderId
     * @param skuId
     * @param sn
     * @param whsId
     * @param servletRequest
     * @param servletResponse
     * @return
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public Result<ScanResponse> scanSn(Integer gdnHeaderId, Integer skuId, String sn, Integer whsId, HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws Exception {
        String loginName = HttpSessionUtil.getLoginName(servletRequest);
        if (whsId==null){
            Shift.fatal("请选择出库仓库");
        }
        Warehouse warehouse = warehouseMapper.selectOne(new QueryWrapper<Warehouse>().eq("WHS_ID", whsId));
        if (warehouse==null){
            Shift.fatal("请选择出库仓库");
        }
        Goods goods = goodsMapper.selectBySn(sn);
        if (goods == null) {
            Shift.fatal(String.format("未查找到该物料[%s]的相关信息", sn));
        }
        if (goods.getStatus().equals(GoodsStatusEnum.HAS_DOWN.getCode())) {
            Shift.fatal(String.format("该物料[%s]的状态是[%s]，已完成拣料下架操作", sn, GoodsStatusEnum.HAS_DOWN.getEnumName()));
        }
        if (goods.getWhsId()==null){
            Shift.fatal("["+sn+"]未判检入库，无法拣货!");
        }
        if (goods.getIsHold().equals(IsHoldEnum.Yes.code)){
            Shift.fatal("["+sn+"]已冻结，无法拣货!");
        }

        // 物料所在仓库必须和出库单发料仓库一致
        if (!Objects.equals(goods.getWhsId(), whsId)) {
            Warehouse whsGoods = warehouseMapper.selectById(goods.getWhsId());
            if (whsGoods==null){
                Shift.fatal("无仓库匹配信息，请联系管理员!");
            }
            Warehouse whs = warehouseMapper.selectById(whsId);
            if (whs==null){
                Shift.fatal("无仓库匹配信息，请联系管理员!");
            }
            Shift.fatal(String.format("SN[%s]所属仓库[%s]和发料仓库[%s]不一致，不能拣料！", sn, whsGoods.getWhsName(), whs.getWhsName()));
        }


        // 物料状态(status)必须为2(待上架)、3(已上架)、4(已下架)
        if (!Objects.equals(goods.getStatus(), GoodsStatusEnum.WAIT_UP.getCode()) &&
        		!Objects.equals(goods.getStatus(), GoodsStatusEnum.HAS_UP.getCode()) &&
        		!Objects.equals(goods.getStatus(), GoodsStatusEnum.WAIT_DOWM.getCode())) {
            GoodsStatusEnum goodsStatusEnum = GoodsStatusEnum.getEnumByCode(goods.getStatus());
            Shift.fatal(String.format("SN[%s]状态为[%s]，不能拣料！", sn, goodsStatusEnum.getEnumName()));
        }

        // 物料编码必须和当前的要求一致
        if (!Objects.equals(goods.getSkuId(), skuId)) {
            Sku sku = skuMapper.selectById(goods.getSkuId());
            Shift.fatal(String.format("SN[%s]的物料编码[%s]与当前拣料需求不一致，不能拣料！", sn, sku.getSkuCode()));
        }

        // 发货单单头
        GdnHeader gdnHeader = gdnHeaderMapper.selectById(gdnHeaderId);
        String gdnTypeCheck = gdnHeader.getGdnType();

        //TODO ZYF新加的：如果出库单完成或者取消或者关闭不能再操作了
        if (gdnHeader.getStatus().equals(GdnHeaderStatusEnum.CANCEL.code)||
                gdnHeader.getStatus().equals(GdnHeaderStatusEnum.FINISH.code)||
                gdnHeader.getStatus().equals(GdnHeaderStatusEnum.CLOSE.code)){
            Shift.fatal(gdnHeader.getGdnNo()+"已完成或取消，无法拣料!");
        }

        //TODO ZYF:如果出库单单据类型为D16，扫描SN必须在源单据报废调拨的条码下
        if (gdnTypeCheck.equals(GdnTypeEnum.D16.code)){

            List<String> sourceSn = pickTaskMapper.selectDataSourceSn(gdnHeader.getDataSource());
            if (CollectionUtil.isEmpty(sourceSn)){
                Shift.fatal("扫描条码不属于出库单的源报废单!");
            }
            if (!sourceSn.contains(sn)){
                Shift.fatal("扫描条码不属于出库单的源报废单!");
            }
        }
        //OA不合格评审出库 需要校验条码是否在源单号里面
        if (gdnTypeCheck.equals(GdnTypeEnum.D22.code)){

            List<String> sourceSn = pickTaskMapper.selectGoodsExitFromAsn(gdnHeader.getSourceShipNotice(),skuId,sn);
            if (CollectionUtil.isEmpty(sourceSn)){
                Shift.fatal("扫描条码不属于来源单号:"+gdnHeader.getSourceShipNotice());
            }

        }

        // 工厂代码必须和当前的要求一致
        //TODO ZYF:如果单据类型为D02/D21/D28，则不校验工厂
        if ((gdnTypeCheck.equals(GdnTypeEnum.D02.code))||(gdnTypeCheck.equals(GdnTypeEnum.D21.code))
                ||(gdnTypeCheck.equals(GdnTypeEnum.D28.code))){
        }else {
            if (!Objects.equals(gdnHeader.getFactory(), goods.getFactory())) {
                Shift.fatal(String.format("SN[%s]的工厂代码[%s]与当前拣料需求不一致，不能拣料！", sn, goods.getFactory()));
            }
        }

        // 扫描的SN的物料数量不能超过剩余需求数量
        QueryWrapper<GdnDtl> gdnDtlWrapper = new QueryWrapper<>();
        gdnDtlWrapper.eq("GDN_HEADER_ID", gdnHeaderId);
        gdnDtlWrapper.eq("SKU_ID", goods.getSkuId());
        gdnDtlWrapper.last("AND ORDER_QTY > PICK_QTY ORDER BY GDN_LINE_NO ASC");

        List<GdnDtl> gdnDtlList = gdnDtlMapper.selectList(gdnDtlWrapper);
        if (gdnDtlList.size() == 0) {
            Shift.fatal("该发货单已拣货完成!");
        }

        if (gdnDtlList.get(0).getWhsId()==null){
            Shift.fatal("请选择出库仓!");
        }

        // 物料品质状态(qa_status)必须和出库单明细的品质状态(qa_status)一致
        GdnDtl firstGdnDtl = gdnDtlList.get(0);
        if (!Objects.equals(firstGdnDtl.getQaStatus(), goods.getQaStatus())) {
            QaStatusEnum qaStatusEnum = QaStatusEnum.getEnumByCode(goods.getQaStatus());
            Shift.fatal(String.format("SN[%s]品质状态为[%s]，不符合要求，不能拣料！", goods.getGoodsSn(), qaStatusEnum.getEnumName()));
        }

        BigDecimal orderQty = gdnDtlList.stream().map(s -> s.getOrderQty()).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal pickQty = gdnDtlList.stream().map(s -> s.getPickQty()).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal remainQty = orderQty.subtract(pickQty);

        Result<ScanResponse> result = new Result<ScanResponse>();
        ScanResponse scanResponse = new ScanResponse();
        scanResponse.setSn(sn);
        scanResponse.setCount(goods.getQty());
        result.setData(scanResponse);

        // 扫描的SN的物料数量不能超过剩余需求数量，否则弹出【物料拆分】窗口
        if (goods.getQty().compareTo(remainQty) == 1) {
            result.setFlag(false);
            result.setMessage(String.format("当前物料[%s]的数量超出需求数量，是否将当前物料进行拆分", sn));
        } else {
            // 抽取拣料服务
            pick(goods, gdnDtlList, loginName, new Date(),gdnHeader);
        }
//        Shift.fatal("testet");
        return result;
    }

    /**
     * 拣料服务
     *
     * @param loginName
     * @param goods
     * @param gdnDtls
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void pickOld(Goods goods, List<GdnDtl> gdnDtls, String loginName, Date date) throws Exception {


        // 拣料服务
        // 更新货品表(wms_goods) 更新内容：status=5(已拣选)
        Goods goodsUpdate = new Goods();
        goodsUpdate.setGoodsId(goods.getGoodsId());
        goodsUpdate.setModifyTime(new Date());
        goodsUpdate.setModifyUser(loginName);
        goodsUpdate.setStatus(GoodsStatusEnum.HAS_DOWN.getCode());
        goodsMapper.updateById(goodsUpdate);


        //一个SN只能扫在一张出库单扫码一次
        List<GdnDtl> chenckGdnDtls = gdnDtlMapper.selectList(new QueryWrapper<GdnDtl>()
                .eq("GDN_HEADER_ID", gdnDtls.get(0).getGdnHeaderId()));
        List<Long> chenckDtlIds = chenckGdnDtls.stream().map(t -> t.getGdnDtlId()).collect(Collectors.toList());
        List<GdnDtlGoods> checkDtlGoodsList = gdnDtlGoodsMapper.selectList(new QueryWrapper<GdnDtlGoods>()
                .in("GDN_DTL_ID", chenckDtlIds)
                .eq("GOODS_SN", goods.getGoodsSn()));
        if (!CollectionUtil.isEmpty(checkDtlGoodsList)){
            Shift.fatal("同一出库单不能出现重复的SN！");
        }

        BigDecimal qty = goods.getQty();

        for (GdnDtl gdnDtl : gdnDtls) {
            if (qty.signum() == 0) {
                break;
            }

            BigDecimal dtlOrderQty = gdnDtl.getOrderQty();
            BigDecimal dtlPickQty = gdnDtl.getPickQty();
            //这里有问题的，注释掉
            /*if (dtlOrderQty.compareTo(dtlPickQty) == 0) {
                continue;
            }*/

            // 该明细下剩余可拣的数量
            BigDecimal dtlRemainQty = dtlOrderQty.subtract(dtlPickQty);

            BigDecimal updateQty = BigDecimal.ZERO;

            if (dtlRemainQty.compareTo(qty) == 0) {
                updateQty = qty;
                int rowU = gdnDtlMapper.updatePickQtyByPk(gdnDtl.getGdnDtlId(), qty);
                qty = BigDecimal.ZERO;
                if (rowU != 1) {
                    Shift.fatal("拣料数不能超出明细的计划数");
                }
            } else if (dtlRemainQty.compareTo(qty) == 1) {
                updateQty = qty;
                int rowU = gdnDtlMapper.updatePickQtyByPk(gdnDtl.getGdnDtlId(), qty);
                qty = BigDecimal.ZERO;
                if (rowU != 1) {
                    Shift.fatal("拣料数不能超出明细的计划数");
                }
            } else if (dtlRemainQty.compareTo(qty) == -1) {
                qty = qty.subtract(dtlRemainQty);
                updateQty = dtlRemainQty;
                int rowU = gdnDtlMapper.updatePickQtyByPk(gdnDtl.getGdnDtlId(), dtlRemainQty);
                if (rowU != 1) {
                    Shift.fatal("拣料数不能超出明细的计划数");
                }
            }

            //写发货单明细货品清单表(wms_gdn_dtl_goods)
            GdnDtlGoods gdnDtlGoods = new GdnDtlGoods();
            gdnDtlGoods.setGoodsSn(goods.getGoodsSn());
            gdnDtlGoods.setGoodsId(goods.getGoodsId());
            gdnDtlGoods.setQaStatus(gdnDtl.getQaStatus());
            gdnDtlGoods.setQty(updateQty);
            gdnDtlGoods.setStatus(1);
            gdnDtlGoods.setCreateTime(new Date());
            gdnDtlGoods.setCreateUser(loginName);
            gdnDtlGoods.setGdnDtlId(gdnDtl.getGdnDtlId());

            gdnDtlGoodsMapper.insert(gdnDtlGoods);


            //TODO 可能有并发脏读风险
            //todo 若pick_qty<order_qty且ship_qty=0,则取4(拣货中)，
            //todo 若pick_qty=order_qty且ship_qty=0,则取5(待装柜)
            GdnDtl gdnDtlNew = gdnDtlMapper.selectById(gdnDtl.getGdnDtlId());
            BigDecimal pickQty = gdnDtlNew.getPickQty();
            BigDecimal orderQty = gdnDtlNew.getOrderQty();
            BigDecimal shipQty = gdnDtlNew.getShipQty();

            GdnDtl gdnDtlUpStatus = new GdnDtl();
            gdnDtlUpStatus.setModifyUser(loginName);
            gdnDtlUpStatus.setModifyTime(new Date());
            gdnDtlUpStatus.setGdnDtlId(gdnDtl.getGdnDtlId());

            if (pickQty.compareTo(orderQty) == -1 && shipQty.compareTo(BigDecimal.ZERO) == 0) {
                gdnDtlUpStatus.setStatus(GdnDtlStatusEnum.PICKING.getCode());
                gdnDtlMapper.updateById(gdnDtlUpStatus);
            } else if (pickQty.compareTo(orderQty) == 0 && shipQty.compareTo(BigDecimal.ZERO) == 0) {
                gdnDtlUpStatus.setStatus(GdnDtlStatusEnum.WAITLOAD.getCode());
                gdnDtlMapper.updateById(gdnDtlUpStatus);
            }

        }
        // 更新发货单表头(wms_gdn_header)
        // 若status=1(新建)或2(已审核)，更新为3(进行中)
        GdnDtl gdnDtl = gdnDtls.get(0);
        GdnHeader gdnHeader = gdnHeaderMapper.selectById(gdnDtl.getGdnHeaderId());
        int createCode = GdnHeaderStatusEnum.CREATE.getCode();
        int hasdoneCode = GdnHeaderStatusEnum.HASDONE.getCode();

        List<Integer> statusList = Arrays.asList(createCode, hasdoneCode);

        if (statusList.contains(gdnHeader.getStatus())) {
            GdnHeader gdnHeaderUpdate = new GdnHeader();
            gdnHeaderUpdate.setGdnHeaderId(gdnHeader.getGdnHeaderId());
            gdnHeaderUpdate.setStatus(GdnHeaderStatusEnum.DOING.getCode());
            gdnHeaderUpdate.setModifyTime(new Date());
            gdnHeaderUpdate.setModifyUser(loginName);
            gdnHeaderMapper.updateById(gdnHeaderUpdate);
        }


        // 更新库存
        // 库位
        UpdateInventoryInputDto inputLocDto = new UpdateInventoryInputDto();
        inputLocDto.setAlocQty(goods.getQty());
        inputLocDto.setWhsId(goods.getWhsId());
        inputLocDto.setLocId(goods.getLocId());
        inputLocDto.setOwner(goods.getOwner());
        inputLocDto.setSkuId(goods.getSkuId());
        inputLocDto.setFactory(goods.getFactory());
        inputLocDto.setVendorId(goods.getVendorId());
        inputLocDto.setLotNo(goods.getLotno());
        inputLocDto.setInvAttr(goods.getInvAttr());
        inputLocDto.setQaStatus(goods.getQaStatus());
        inputLocDto.setOperator(loginName);
        inputLocDto.setUnit(goods.getUnit());
        inventoryService.updateLocInventroy(inputLocDto);

        // 库区
        UpdateInventoryInputDto inputZoneDto = new UpdateInventoryInputDto();
        inputZoneDto.setAlocQty(goods.getQty());
        inputZoneDto.setWhsId(goods.getWhsId());
        inputZoneDto.setLocId(goods.getLocId());
        inputZoneDto.setOwner(goods.getOwner());
        inputZoneDto.setSkuId(goods.getSkuId());
        inputZoneDto.setFactory(goods.getFactory());
        inputZoneDto.setVendorId(goods.getVendorId());
        inputZoneDto.setLotNo(goods.getLotno());
        inputZoneDto.setInvAttr(goods.getInvAttr());
        inputZoneDto.setQaStatus(goods.getQaStatus());
        inputZoneDto.setOperator(loginName);
        inputZoneDto.setUnit(goods.getUnit());
        inventoryService.updateZoneInventory(inputZoneDto);

        // 拣料
        Goods goodsUp = goodsMapper.selectBySn(goods.getGoodsSn());
        GoodsLog goodsLog = new GoodsLog();
        goodsLog.setGoodsSn(goods.getGoodsSn());
        goodsLog.setComments(GoodsLogEnum.RAW_PICK.getEnumName());
        goodsLog.setOpCode(GoodsLogEnum.RAW_PICK.getEnumName());
        goodsLog.setOperator(loginName);
        goodsLog.setCreateTime(new Date());
        goodsLog.setCreateUser(loginName);
        goodsLog.setUpdateContent("未更新:" + JSON.toJSONString(goods) + ",更新后:" + JSON.toJSONString(goodsUp));
        goodsLogMapper.insert(goodsLog);

        // 调用SAP接口【开始拣料--->当前单子扫进第一个条码的时候调用】
        int goodsSnSize = gdnDtlGoodsMapper.selectCountSnGdn(gdnHeader.getGdnHeaderId());
        if (goodsSnSize == 1) {
            try {
                ZDT_076_ReqItems record = new ZDT_076_ReqItems();
                record.setBILLTYPE("1"); 	// 默认1
                record.setBILLNO(gdnHeader.getGdnNo());
                record.setZDATE(DateUtil.format(date, "yyyyMMdd"));
                record.setZTIME(DateUtil.format(date, "HHmmss"));
                record.setSTATUS("1"); 		// 1确认、2取消
                record.setZNAME(loginName);
                wmsBillStatusBackService.backToSap(record);
            } catch (SystemException e) {
                Shift.fatal(e.getMessage());
            } catch (Exception e) {
                Shift.fatal("调用SAP接口失败");
            }
        }
    }


    //TODO ZYF:实在受不了了，重写他代码
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void pick(Goods goods, List<GdnDtl> gdnDtls, String loginName, Date date,GdnHeader header) throws Exception {
        if (CollectionUtil.isEmpty(gdnDtls)){
            Shift.fatal("查无对应出库明细!");
        }
        //获取系统参数 判断是否需要交接确认 未配置或者配置为true为需要交接确认
        Boolean isRawOutSure = getBasicParam("isRawOutSure");
        //单据新建状态并且不需要交接 更新表头状态， 用于处理中途切换中途作业的订单
        if(header.getStatus()==GdnHeaderStatusEnum.CREATE.code){//新建才更新
            if(!isRawOutSure){
                gdnHeaderMapper.updateNeedSure(header.getGdnHeaderId(),NeedSureEnum.NOT_NEED.code);
            }else{
                gdnHeaderMapper.updateNeedSure(header.getGdnHeaderId(),NeedSureEnum.NEED.code);
            }
        }

        // 拣料服务
        // 更新货品表(wms_goods) 更新内容：status=5(已拣选)
        Goods goodsUpdate = new Goods();
        goodsUpdate.setGoodsId(goods.getGoodsId());
        goodsUpdate.setModifyTime(new Date());
        goodsUpdate.setModifyUser(loginName);
        goodsUpdate.setStatus(GoodsStatusEnum.HAS_DOWN.getCode());
        goodsMapper.updateById(goodsUpdate);

        List<Long> dtlIds = gdnDtls.stream().map(t -> t.getGdnDtlId()).collect(Collectors.toList());
        //再查一遍明细【从大到小排序】
        gdnDtls=gdnDtlMapper.selectRawGdnDtl(dtlIds);
        BigDecimal zero=BigDecimal.ZERO;
        for (GdnDtl gdnDtl : gdnDtls) {
            if (gdnDtl.getPickQty()==null) {
                gdnDtl.setPickQty(zero);
            }
        }

        BigDecimal qty = goods.getQty();
        int flag=0;
        //处理明细
        for (GdnDtl gdnDtl : gdnDtls) {
            //当前明细最大拣料数
            BigDecimal maxPickQty = gdnDtl.getOrderQty().subtract(gdnDtl.getPickQty());
            BigDecimal updateQty=zero;
            if (maxPickQty.compareTo(qty)>-1){
                //maxPickQty>=qty
                updateQty=qty;
                qty=zero;
            }else {
                //maxPickQty<qty
                updateQty=maxPickQty;
                qty=qty.subtract(maxPickQty);
            }
            int rowU = gdnDtlMapper.updatePickQtyByPk(gdnDtl.getGdnDtlId(), updateQty);
            if (rowU != 1) {
                Shift.fatal("拣料数不能超出明细的计划数");
            }
            flag++;

            //写发货单明细货品清单表(wms_gdn_dtl_goods)
            GdnDtlGoods gdnDtlGoods = new GdnDtlGoods();
            gdnDtlGoods.setGoodsSn(goods.getGoodsSn());
            gdnDtlGoods.setGoodsId(goods.getGoodsId());
            gdnDtlGoods.setQaStatus(gdnDtl.getQaStatus());
            gdnDtlGoods.setQty(updateQty);
            gdnDtlGoods.setStatus(1);
            gdnDtlGoods.setCreateTime(new Date());
            gdnDtlGoods.setCreateUser(loginName);
            gdnDtlGoods.setGdnDtlId(gdnDtl.getGdnDtlId());
            gdnDtlGoodsMapper.insert(gdnDtlGoods);

            //todo 若pick_qty<order_qty且ship_qty=0,则取4(拣货中)，
            //todo 若pick_qty=order_qty且ship_qty=0,则取5(待装柜)
            GdnDtl gdnDtlNew = gdnDtlMapper.selectById(gdnDtl.getGdnDtlId());
            BigDecimal pickQty = gdnDtlNew.getPickQty();
            BigDecimal orderQty = gdnDtlNew.getOrderQty();
            BigDecimal shipQty = gdnDtlNew.getShipQty();

            GdnDtl gdnDtlUpStatus = new GdnDtl();
            gdnDtlUpStatus.setModifyUser(loginName);
            gdnDtlUpStatus.setModifyTime(new Date());
            gdnDtlUpStatus.setGdnDtlId(gdnDtl.getGdnDtlId());
            if (pickQty.compareTo(orderQty) == -1 && shipQty.compareTo(BigDecimal.ZERO) == 0) {
                gdnDtlUpStatus.setStatus(GdnDtlStatusEnum.PICKING.getCode());
                gdnDtlMapper.updateById(gdnDtlUpStatus);
            } else if (pickQty.compareTo(orderQty) == 0 && shipQty.compareTo(BigDecimal.ZERO) == 0) {
                gdnDtlUpStatus.setStatus(GdnDtlStatusEnum.WAITLOAD.getCode());
                gdnDtlMapper.updateById(gdnDtlUpStatus);
            }

            if (qty.signum()==0){
                break;
            }
        }

        if (qty.compareTo(zero)==1){
            Shift.fatal("拣料数大于需求数，请拆分SN!");
        }

        if (flag==0){
            Shift.fatal("没有符合出库明细!");
        }

        //限制住bug
        //查询拣料后的sn
        List<GdnDtlGoods> checkDtlGoodsList = gdnDtlGoodsMapper.selectList(new QueryWrapper<GdnDtlGoods>().
                in("GDN_DTL_ID", dtlIds).eq("GOODS_SN", goods.getGoodsSn()));
        if (CollectionUtil.isEmpty(checkDtlGoodsList)){
            Shift.fatal("系统堵塞，请重新扫码！");
        }
        for (GdnDtlGoods checkDtlGoods : checkDtlGoodsList) {
            if (checkDtlGoods.getQty().signum()==0){
                Shift.fatal("系统堵塞，请重新扫码！");
            }
        }



        // 更新发货单表头(wms_gdn_header)
        // 若status=1(新建)或2(已审核)，更新为3(进行中)
        GdnDtl gdnDtl = gdnDtls.get(0);
        GdnHeader gdnHeader = gdnHeaderMapper.selectById(gdnDtl.getGdnHeaderId());
        int createCode = GdnHeaderStatusEnum.CREATE.getCode();
        int hasdoneCode = GdnHeaderStatusEnum.HASDONE.getCode();

        List<Integer> statusList = Arrays.asList(createCode, hasdoneCode);

        if (statusList.contains(gdnHeader.getStatus())) {
            GdnHeader gdnHeaderUpdate = new GdnHeader();
            gdnHeaderUpdate.setGdnHeaderId(gdnHeader.getGdnHeaderId());
            gdnHeaderUpdate.setStatus(GdnHeaderStatusEnum.DOING.getCode());
            gdnHeaderUpdate.setModifyTime(new Date());
            gdnHeaderUpdate.setModifyUser(loginName);
            gdnHeaderMapper.updateById(gdnHeaderUpdate);
        }


        // 更新库存
        // 库位
        UpdateInventoryInputDto inputLocDto = new UpdateInventoryInputDto();
        inputLocDto.setAlocQty(goods.getQty());
        inputLocDto.setWhsId(goods.getWhsId());
        inputLocDto.setLocId(goods.getLocId());
        inputLocDto.setOwner(goods.getOwner());
        inputLocDto.setSkuId(goods.getSkuId());
        inputLocDto.setFactory(goods.getFactory());
        inputLocDto.setVendorId(goods.getVendorId());
        inputLocDto.setLotNo(goods.getLotno());
        inputLocDto.setInvAttr(goods.getInvAttr());
        inputLocDto.setQaStatus(goods.getQaStatus());
        inputLocDto.setOperator(loginName);
        inputLocDto.setUnit(goods.getUnit());
        inventoryService.updateLocInventroy(inputLocDto);

        // 库区
        UpdateInventoryInputDto inputZoneDto = new UpdateInventoryInputDto();
        inputZoneDto.setAlocQty(goods.getQty());
        inputZoneDto.setWhsId(goods.getWhsId());
        inputZoneDto.setLocId(goods.getLocId());
        inputZoneDto.setOwner(goods.getOwner());
        inputZoneDto.setSkuId(goods.getSkuId());
        inputZoneDto.setFactory(goods.getFactory());
        inputZoneDto.setVendorId(goods.getVendorId());
        inputZoneDto.setLotNo(goods.getLotno());
        inputZoneDto.setInvAttr(goods.getInvAttr());
        inputZoneDto.setQaStatus(goods.getQaStatus());
        inputZoneDto.setOperator(loginName);
        inputZoneDto.setUnit(goods.getUnit());
        inventoryService.updateZoneInventory(inputZoneDto);

        // 拣料
        Goods goodsUp = goodsMapper.selectBySn(goods.getGoodsSn());
        GoodsLog goodsLog = new GoodsLog();
        goodsLog.setGoodsSn(goods.getGoodsSn());
        goodsLog.setComments(GoodsLogEnum.RAW_PICK.getEnumName());
        goodsLog.setOpCode(GoodsLogEnum.RAW_PICK.getEnumName());
        goodsLog.setOperator(loginName);
        goodsLog.setCreateTime(new Date());
        goodsLog.setCreateUser(loginName);
        goodsLog.setUpdateContent("未更新:" + JSON.toJSONString(goods) + ",更新后:" + JSON.toJSONString(goodsUp));
        goodsLogMapper.insert(goodsLog);

        // 调用SAP接口【开始拣料--->当前单子扫进第一个条码的时候调用】
        int goodsSnSize = gdnDtlGoodsMapper.selectCountSnGdn(gdnHeader.getGdnHeaderId());
        if (goodsSnSize == 1) {
            try {
                ZDT_076_ReqItems record = new ZDT_076_ReqItems();
                record.setBILLTYPE("1"); 	// 默认1
                record.setBILLNO(gdnHeader.getGdnNo());
                record.setZDATE(DateUtil.format(date, "yyyyMMdd"));
                record.setZTIME(DateUtil.format(date, "HHmmss"));
                record.setSTATUS("1"); 		// 1确认、2取消
                record.setZNAME(loginName);
                wmsBillStatusBackService.backToSap(record);
            } catch (SystemException e) {
                Shift.fatal(e.getMessage());
            } catch (Exception e) {
                Shift.fatal("调用SAP接口失败");
            }
        }
    }

    /**
     * 推荐库位集合查询
     *
     * @param whsId
     * @param skuId
     * @param gdnHeaderId
     * @param servletRequest
     * @param servletResponse
     * @return
     */
    public Result<List<LocResponse>> getLoc(Integer whsId, Integer skuId, Integer gdnHeaderId, HttpServletRequest servletRequest, HttpServletResponse servletResponse) {

        Result<List<LocResponse>> result = new Result<>();

        List<LocResponse> locResponseList = goodsMapper.selectAllLocIdBySkuId(skuId, null);
        if (locResponseList.size() == 0) {
            return result;
        }

//        Location location = locationMapper.selectOneByLocCode(locCode);
//        locResponseList = locResponseList.stream().filter(s -> s.getLocId().equals(location.getLocId())).collect(Collectors.toList());

//        //TODO 处理SQL IN 1000异常
//        if (locResponseList.size() > 1000) {
//
//        }

        this.getRecommendLoc(whsId, skuId, gdnHeaderId, locResponseList);
        if (!CollectionUtil.isEmpty(locResponseList)){
            locResponseList=locResponseList.stream().filter(t ->
                    t.getTotalQty()!=null).collect(Collectors.toList());
            locResponseList=locResponseList.stream().filter(t ->
                    t.getTotalQty().compareTo(BigDecimal.ZERO)==1).collect(Collectors.toList());
        }


        result.setData(locResponseList);
        return result;
    }

    /**
     * 获取推荐库位
     *
     * @param whsId
     * @param skuId
     * @param gdnHeaderId
     * @param locResponseList
     */
    private void getRecommendLoc(Integer whsId, Integer skuId, Integer gdnHeaderId, List<LocResponse> locResponseList) {
        GdnHeader gdnHeader = gdnHeaderMapper.selectById(gdnHeaderId);

        //创建类型的条件构造器
        QueryWrapper<GdnDtl> wrapperDtl = new QueryWrapper<>();
        wrapperDtl.eq("GDN_HEADER_ID", gdnHeaderId);
        wrapperDtl.eq("SKU_ID", skuId);
        List<GdnDtl> gdnDtlList = gdnDtlMapper.selectList(wrapperDtl);
        List<Integer> vendorList = gdnDtlList.stream().map(s -> s.getVendor()).filter(s -> s != null).distinct().collect(Collectors.toList());
        List<String> invAttrList = gdnDtlList.stream().map(s -> s.getInvAttr()).filter(s -> s != null).distinct().collect(Collectors.toList());
        List<Integer> qaStatusList = gdnDtlList.stream().map(s -> s.getQaStatus()).distinct().filter(s -> s != null).collect(Collectors.toList());
        List<String> lotnoList = gdnDtlList.stream().map(s -> s.getLotno()).filter(s -> s != null).distinct().collect(Collectors.toList());

        System.out.println(locResponseList);
//        List<Integer> locIdList = locResponseList.stream().filter(s -> s.getLocId() != null).map(s -> s.getLocId()).distinct().collect(Collectors.toList());
        List<Integer> locIdList = locResponseList.stream().filter(s -> s.getLocId() != null).map(s -> s.getLocId()).distinct().collect(Collectors.toList());



        List<InvLoc> invLocs=new ArrayList<>();

        if (!CollectionUtil.isEmpty(locIdList)){
            //创建类型的条件构造器
            QueryWrapper<InvLoc> wrapper = new QueryWrapper<>();
            wrapper.in("LOC_ID", locIdList);
            wrapper.eq("WHS_ID", whsId);
            wrapper.eq("OWNER", gdnHeader.getOwner());
            wrapper.eq("FACTORY", gdnHeader.getFactory());
            wrapper.eq("SKU_ID", skuId);

            wrapper.in(vendorList.size() != 0, "VENDOR_ID", vendorList);
            wrapper.in(invAttrList.size() != 0, "INV_ATTR", invAttrList);
            wrapper.in(qaStatusList.size() != 0, "QA_STATUS", qaStatusList);
            wrapper.in(lotnoList.size() != 0, "LOTNO", lotnoList);
            invLocs = invLocMapper.selectList(wrapper);
        }


        List<LocResponse> nullLocList = locResponseList.stream().filter(s -> s.getLocId() == null).collect(Collectors.toList());
        if (nullLocList.size() > 0) {

            QueryWrapper<InvLoc> wrapperNull = new QueryWrapper<>();
            wrapperNull.isNull("LOC_ID");

            wrapperNull.eq("WHS_ID", whsId);
            wrapperNull.eq("OWNER", gdnHeader.getOwner());
            wrapperNull.eq("FACTORY", gdnHeader.getFactory());
            wrapperNull.eq("SKU_ID", skuId);

            wrapperNull.in(vendorList.size() != 0, "VENDOR_ID", vendorList);
            wrapperNull.in(invAttrList.size() != 0, "INV_ATTR", invAttrList);
            wrapperNull.in(qaStatusList.size() != 0, "QA_STATUS", qaStatusList);
            wrapperNull.in(lotnoList.size() != 0, "LOTNO", lotnoList);

            List<InvLoc> nullInvLocList = invLocMapper.selectList(wrapperNull);
            nullInvLocList.stream().forEach(s -> s.setLocId(-1));
            invLocs.addAll(nullInvLocList);
        }

//        Map<Integer, List<LocResponse>> locIdMap = locResponseList.stream().collect(Collectors.groupingBy(s -> s.getLocId()));
//
//        if (locIdMap.containsKey(null)) {
//            System.out.println("1");
//        }


        List<Location> locationList =new ArrayList<>();
        Map<Integer, List<Location>> mapLoc=new HashMap<>();
        Map<Integer, List<InvLoc>> map=new HashMap<>();

        if (!CollectionUtil.isEmpty(locIdList)){
            //创建类型的条件构造器
            QueryWrapper<Location> wrapperLoc = new QueryWrapper<>();
            wrapperLoc.in("LOC_ID", locIdList);

            locationList = locationMapper.selectList(wrapperLoc);
            mapLoc = locationList.stream().collect(Collectors.groupingBy(s -> s.getLocId()));

            map = invLocs.stream().collect(Collectors.groupingBy(s -> s.getLocId()));
        }



        for (LocResponse locResponse : locResponseList) {

            if (locResponse != null && map.containsKey(locResponse.getLocId())) {
                List<InvLoc> invLocList = map.get(locResponse.getLocId());

                BigDecimal qty = invLocList.stream().map(s -> s.getQty()).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal preOutQty = invLocList.stream().map(s -> s.getPreOutQty()).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal holdQty = invLocList.stream().map(s -> s.getHoldQty()).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal alocQty = invLocList.stream().map(s -> s.getAlocQty()).reduce(BigDecimal.ZERO, BigDecimal::add);

                BigDecimal total = qty.subtract(preOutQty).subtract(holdQty).subtract(alocQty);

                locResponse.setCycleQty(total);
                locResponse.setTotalQty(total);

                if (mapLoc.containsKey(locResponse.getLocId())) {
                    List<Location> locations = mapLoc.get(locResponse.getLocId());
                    Location location = locations.get(0);
                    locResponse.setLocCode(location.getLocCode());
                }
            }
            if (locResponse.getLocId() == null) {
                List<InvLoc> invLocList = map.get(-1);

                if (invLocList!=null){
                    BigDecimal qty = invLocList.stream().map(s -> ((s==null||s.getQty()==null)?BigDecimal.ZERO:s.getQty())).reduce(BigDecimal.ZERO, BigDecimal::add);
                    BigDecimal preOutQty = invLocList.stream().map(s -> ((s==null||s.getPreOutQty()==null)?BigDecimal.ZERO:s.getPreOutQty())).reduce(BigDecimal.ZERO, BigDecimal::add);
                    BigDecimal holdQty = invLocList.stream().map((s -> (s==null||s.getHoldQty()==null)?BigDecimal.ZERO:s.getHoldQty())).reduce(BigDecimal.ZERO, BigDecimal::add);
                    BigDecimal alocQty = invLocList.stream().map(s -> ((s==null||s.getAlocQty()==null)?BigDecimal.ZERO:s.getAlocQty())).reduce(BigDecimal.ZERO, BigDecimal::add);

                    BigDecimal total = qty.subtract(preOutQty).subtract(holdQty).subtract(alocQty);

                    locResponse.setCycleQty(total);
                    locResponse.setTotalQty(total);

                    locResponse.setLocCode("待上架");
//                if (mapLoc.containsKey(locResponse.getLocId())) {
//                    List<Location> locations = mapLoc.get(locResponse.getLocId());
//                    Location location = locations.get(0);
//
//                }
                }
            }
        }

    }

    public Result<List<PickSkuResponse>> getPickDtlInfo(Integer gdnHeaderId, String skuCode, HttpServletRequest servletRequest, HttpServletResponse servletResponse) {

        Result<List<PickSkuResponse>> result = new Result<>();
        String loginName = HttpSessionUtil.getLoginName(servletRequest);
//        String loginName = "gxm";
        //查询此人
        //仅查看自己的
        GetUserWhsZoneWorkDivisionDto dto = new GetUserWhsZoneWorkDivisionDto();
        dto.setUserName(loginName);

        List<UserWhsZoneWorkDivisionResponse> whsZoneWorkDivisionResponseList = userWorkDivisonMapper.getUserWhsZoneWorkDivision(dto);
        if (whsZoneWorkDivisionResponseList.size() == 0) {
            return result;
        }


        List<Integer> workDivisionDtlIdList = whsZoneWorkDivisionResponseList.stream().map(s -> s.getWorkDivisionDtlId()).collect(Collectors.toList());
        //mybatis已处理SQL的1000in异常
        List<WorkDivisionExtSku> workDivisionExtSkuList = workDivisionExtSkuMapper.selectByWorkDivisionDtlIdList(workDivisionDtlIdList);
        //该员工负责的物料ID集合
        List<Integer> skuIdList = workDivisionExtSkuList.stream().map(s -> s.getSkuId()).collect(Collectors.toList());


        //创建类型的条件构造器
        QueryWrapper<GdnDtl> wrapper = new QueryWrapper<>();
        wrapper.eq("GDN_HEADER_ID", gdnHeaderId);
        if (skuCode != null) {
            SkuBaseResponse skuByCode = skuMapper.getSkuByCode(skuCode);
            wrapper.eq("SKU_ID", skuByCode.getSkuId());
        }

        List<GdnDtl> gdnDtlList = gdnDtlMapper.selectList(wrapper);

        gdnDtlList = gdnDtlList.stream().filter(s -> skuIdList.contains(s.getSkuId())).collect(Collectors.toList());


        List<PickSkuResponse> pickSkuResponses = new ArrayList<>();

        Map<Integer, List<GdnDtl>> skuMap = gdnDtlList.stream().collect(Collectors.groupingBy(s -> s.getSkuId()));


        for (Map.Entry<Integer, List<GdnDtl>> entry : skuMap.entrySet()) {
            PickSkuResponse pickSkuResponse = new PickSkuResponse();

            List<GdnDtl> dtls = entry.getValue();
            BigDecimal pickQty = dtls.stream().map(s -> s.getPickQty()).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal orderQty = dtls.stream().map(s -> s.getOrderQty()).reduce(BigDecimal.ZERO, BigDecimal::add);

            pickSkuResponse.setOrderQty(orderQty);
            pickSkuResponse.setPickQty(pickQty);
            pickSkuResponse.setSkuId(entry.getKey());

            Sku sku = skuMapper.selectById(entry.getKey());
            pickSkuResponse.setSkuCode(sku.getSkuCode());
            pickSkuResponse.setSkuDesc(sku.getDescCn());

            pickSkuResponses.add(pickSkuResponse);
        }


        result.setData(pickSkuResponses);

        return result;
    }

    /**
     * SN明细查询
     *
     * @param skuId
     * @param gdnHeaderId
     * @param servletRequest
     * @param servletResponse
     * @return
     */
    public Result<List<PickSnDtlResponse>> getSnInfo(Integer skuId, Integer gdnHeaderId, HttpServletRequest servletRequest, HttpServletResponse servletResponse) {

        Result<List<PickSnDtlResponse>> result = new Result<>();

        //创建类型的条件构造器
        QueryWrapper<GdnDtl> wrapper = new QueryWrapper<>();
        wrapper.eq("SKU_ID", skuId);
        wrapper.eq("GDN_HEADER_ID", gdnHeaderId);
        List<GdnDtl> gdnDtlList = gdnDtlMapper.selectList(wrapper);

        List<Long> dtlIdList = gdnDtlList.stream().map(s -> s.getGdnDtlId()).collect(Collectors.toList());


        //创建类型的条件构造器
        QueryWrapper<GdnDtlGoods> wrapperGoods = new QueryWrapper<>();
        wrapperGoods.in("GDN_DTL_ID", dtlIdList);

        List<GdnDtlGoods> gdnDtlGoodsList = gdnDtlGoodsMapper.selectList(wrapperGoods);

        Sku sku = skuMapper.selectById(skuId);

        List<PickSnDtlResponse> pickSnDtlResponses = new ArrayList<>();

        if (!CollectionUtil.isEmpty(gdnDtlGoodsList)){
            Map<String, List<GdnDtlGoods>> gdnDtlGoodsMap = gdnDtlGoodsList.stream().collect(
                    Collectors.groupingBy(t -> t.getGoodsSn() + "#" +
                            t.getStatus(), Collectors.toList()));
            for (Map.Entry<String, List<GdnDtlGoods>> entry : gdnDtlGoodsMap.entrySet()) {
                List<GdnDtlGoods> tempDtlGoods = entry.getValue();
                PickSnDtlResponse pickSnDtlResponse = new PickSnDtlResponse();
                pickSnDtlResponse.setSn(tempDtlGoods.get(0).getGoodsSn());
                pickSnDtlResponse.setSkuId(skuId);
                pickSnDtlResponse.setStatus(tempDtlGoods.get(0).getStatus());
                GdnDtlGoodsStatusEnum statusEnum = GdnDtlGoodsStatusEnum.getEnumByCode(tempDtlGoods.get(0).getStatus());
                if (statusEnum!=null){
                    pickSnDtlResponse.setStatusDesc(statusEnum.enumName);
                }
                pickSnDtlResponse.setSkuCode(sku.getSkuCode());
                pickSnDtlResponse.setSkuDesc(sku.getDescCn());
                BigDecimal tempQty=new BigDecimal(0);
                for (GdnDtlGoods gdnDtlGoods : tempDtlGoods) {
                    tempQty=tempQty.add(gdnDtlGoods.getQty());
                }
                pickSnDtlResponse.setQty(tempQty);
                pickSnDtlResponses.add(pickSnDtlResponse);
            }
        }

       /* for (GdnDtlGoods gdnDtlGoods : gdnDtlGoodsList) {
            PickSnDtlResponse pickSnDtlResponse = new PickSnDtlResponse();
            pickSnDtlResponse.setSn(gdnDtlGoods.getGoodsSn());
            pickSnDtlResponse.setQty(gdnDtlGoods.getQty());
            pickSnDtlResponse.setSkuId(skuId);
            pickSnDtlResponse.setStatus(gdnDtlGoods.getStatus());
            GdnDtlStatusEnum statusEnum = GdnDtlStatusEnum.getEnumByCode(gdnDtlGoods.getStatus());
            pickSnDtlResponse.setStatusDesc(statusEnum.getEnumName());
            pickSnDtlResponse.setSkuCode(sku.getSkuCode());
            pickSnDtlResponse.setSkuDesc(sku.getDescCn());
            pickSnDtlResponses.add(pickSnDtlResponse);
        }*/




        result.setData(pickSnDtlResponses);

        return result;
    }

    /**
     * 拣料状态
     *
     * @return
     */
    public Result<List<RawPickEnum>> getPickMaterialStatusList() {
        RawPickEnum[] values = RawPickEnum.values();
        List<RawPickEnum> rawPickEnumList = Arrays.asList(values);
        Result<List<RawPickEnum>> result = new Result<>();
        result.setData(rawPickEnumList);
        return result;
    }

    /**
     * 取消拣料
     *
     * flag
     * PDA传true             更新明细状态，且回传SAP
     * SAP删改领料单传false  明细状态默认已发运，且不回传SAP
     *
     * @param servletRequest
     * @param servletResponse
     * @return
     */
    @Transactional
    public Result<List<ScanResponse>> cancelPick(List<CancelPickMaterialRequest> list,
    		HttpServletRequest servletRequest, HttpServletResponse servletResponse,Boolean flag,String systemName) throws Exception {
        RLock rLock = redisson.getLock("cancelPickRaw");


        try {
            boolean res = rLock.tryLock(2, 8, TimeUnit.SECONDS);
            if(!res){
                return  new Result<>(false,"请勿重复请求！！");
            }
            String loginName = HttpSessionUtil.getLoginName(servletRequest);
            if (systemName!=null){
                loginName=systemName;
            }
            Date date = new Date();

            List<Integer> goodsIds = new ArrayList<>();
            for (CancelPickMaterialRequest item : list) {
                String sn = item.getSn();
                Long gdnHeaderId = item.getGdnHeaderId();
                Integer skuId = item.getSkuId();

                Goods goods = goodsMapper.selectBySn(sn);
                if (goods == null) {
                    Shift.fatal("没有SN[" + sn + "]的信息!");
                }
                if (!goods.getStatus().equals(GoodsStatusEnum.HAS_DOWN.code)) {
                    Shift.fatal("SN[" + goods.getGoodsSn() + "]状态非[" + GoodsStatusEnum.HAS_DOWN.enumName + "],不能取消拣选!");
                }

                List<Integer> dtlGoodsStatuList = pickTaskMapper.selectGdnDtlGoodsStatus(gdnHeaderId, sn);
                if ( CollectionUtil.isEmpty(dtlGoodsStatuList)){
                    Shift.fatal("["+sn+"]状态非["+GdnDtlGoodsStatusEnum.IS_PICK.enumName+"]，无法取消拣货!");
                }
                if ( !dtlGoodsStatuList.get(0).equals(GdnDtlGoodsStatusEnum.IS_PICK.code)){
                    Shift.fatal("["+sn+"]状态非["+GdnDtlGoodsStatusEnum.IS_PICK.enumName+"]，无法取消拣货!");
                }

                //查询发货单主档状态
                GdnHeader gdnHeader = gdnHeaderMapper.selectOne(new QueryWrapper<GdnHeader>().eq("GDN_HEADER_ID", gdnHeaderId));
                Integer headerStatus = gdnHeader.getStatus();

                List<String > cacheGoodsSnList = deliveryService.getGoodsListCache(gdnHeader.getGdnNo());
                if (!CollectionUtil.isEmpty(cacheGoodsSnList)){
                    if (cacheGoodsSnList.contains(sn)){
                        Shift.fatal("SN["+sn+"]已交接扫描，请完成SN反扫描再取消拣料!");
                    }
                }

                //查询取消拣货的明细信息
                List<CancelPickResponse> cancelPickResponses = pickTaskMapper.selectCancelPickInfo(gdnHeaderId, skuId, goods.getGoodsSn());

                String gdnLine = cancelPickResponses.stream().map(t -> t.getGoodsSn()).collect(Collectors.toList()).stream().collect(Collectors.joining(","));

                //更新货品表(wms_goods)
                Goods goodsUpdate = new Goods();
                goodsIds.add(goods.getGoodsId());
                goodsUpdate.setGoodsId(goods.getGoodsId());
                goodsUpdate.setModifyTime(new Date());
                goodsUpdate.setModifyUser(loginName);
                goodsUpdate.setStatus(GoodsStatusEnum.WAIT_DOWM.getCode());
                int rowGoods = goodsMapper.updateById(goodsUpdate);
                log.info("成功更新{}条记录", rowGoods);
                //更新货品表(wms_goods)

                //待减少的goods数量

                //2）更新发货单明细表(wms_gdn_dtl)
                //pick_qty(拣料数量)减少(分配给本发货单明细的数量)
                //pick_qty_ea(拣料数量ea)减少(分配给本发货单明细的数量)
                //status(状态)：
                //若pick_qty=0，则取1(新建)，
                //若pick_qty<order_qty且ship_qty=0,则取4(拣货中)，
                //若pick_qty=order_qty且ship_qty=0,则取5(待装柜)
                //
                //3）将对应SN从发货单单明细货品清单表(wms_gdn_dtl_goods)删除
                //创建类型的条件构造器

                //todo 若pick_qty=0，则取1(新建)，
                //todo 若pick_qty<order_qty且ship_qty=0,则取4(拣货中)，
                //todo 若pick_qty=order_qty且ship_qty=0,则取5(待装柜)

                //更新库存
                //库位
                UpdateInventoryInputDto inputLocDto = new UpdateInventoryInputDto();
                inputLocDto.setAlocQty(goods.getQty().negate());
                inputLocDto.setWhsId(goods.getWhsId());
                inputLocDto.setLocId(goods.getLocId());
                inputLocDto.setOwner(goods.getOwner());
                inputLocDto.setSkuId(goods.getSkuId());
                inputLocDto.setFactory(goods.getFactory());
                inputLocDto.setVendorId(goods.getVendorId());
                inputLocDto.setLotNo(goods.getLotno());
                inputLocDto.setInvAttr(goods.getInvAttr());
                inputLocDto.setQaStatus(goods.getQaStatus());
                inputLocDto.setUnit(goods.getUnit());
                inputLocDto.setOperator(loginName);
                inventoryService.updateLocInventroy(inputLocDto);

                //库区
                inventoryService.updateZoneInventory(inputLocDto);

                if (goods.getLocId()!=null){
                    // 如SN原库位不为空，扣除原库位库存，添加空库位库存
                    UpdateInventoryInputDto inputLocDtoSub = new UpdateInventoryInputDto();
                    inputLocDtoSub.setQty(goods.getQty().negate());
                    inputLocDtoSub.setWhsId(goods.getWhsId());
                    inputLocDtoSub.setLocId(goods.getLocId());
                    inputLocDtoSub.setOwner(goods.getOwner());
                    inputLocDtoSub.setSkuId(goods.getSkuId());
                    inputLocDtoSub.setFactory(goods.getFactory());
                    inputLocDtoSub.setVendorId(goods.getVendorId());
                    inputLocDtoSub.setLotNo(goods.getLotno());
                    inputLocDtoSub.setInvAttr(goods.getInvAttr());
                    inputLocDtoSub.setQaStatus(goods.getQaStatus());
                    inputLocDtoSub.setUnit(goods.getUnit());
                    inputLocDtoSub.setOperator(loginName);

                    UpdateInventoryInputDto inputLocDtoAdd = new UpdateInventoryInputDto();
                    inputLocDtoAdd.setQty(goods.getQty());
                    inputLocDtoAdd.setWhsId(goods.getWhsId());
                    inputLocDtoAdd.setLocId(null);
                    inputLocDtoAdd.setOwner(goods.getOwner());
                    inputLocDtoAdd.setSkuId(goods.getSkuId());
                    inputLocDtoAdd.setFactory(goods.getFactory());
                    inputLocDtoAdd.setVendorId(goods.getVendorId());
                    inputLocDtoAdd.setLotNo(goods.getLotno());
                    inputLocDtoAdd.setInvAttr(goods.getInvAttr());
                    inputLocDtoAdd.setQaStatus(goods.getQaStatus());
                    inputLocDtoAdd.setUnit(goods.getUnit());
                    inputLocDtoAdd.setOperator(loginName);

                    InvTrans invTrans = new InvTrans();
                    invTrans.setOperator(loginName);
                    invTrans.setCreateUser(loginName);
                    invTrans.setCreateTime(new Date());
                    invTrans.setOpCode(OperateCodeEnum.CREATE.getEnumName());// TODO
                                                                                // 操作
                    invTrans.setDocType(cancelPickResponses.get(0).getGdnType());// 单据类型
                    invTrans.setDocNo(cancelPickResponses.get(0).getGdnNo());
                    invTrans.setDocLineNo(gdnLine);
                    invTrans.setStatus(InvTransStatusEnum.FINISHED.getCode()); // TODO
                                                                                // 0
                                                                                // 未完成，暂时写死
                                                                                // 交易状态
                    invTrans.setTransTime(new Date());// TODO 交易时间
                    invTrans.setOperator(loginName);
                    invTrans.setFmOwner(goods.getOwner());// TODO 货主
                    invTrans.setFmSkuId(goods.getSkuId());
                    invTrans.setFmLocId(goods.getLocId());
                    invTrans.setFmWhsId(goods.getWhsId());
                    invTrans.setFmUnit(goods.getUnit());
                    invTrans.setFmQty(goods.getQty());
                    invTrans.setToOwner(goods.getOwner());
                    invTrans.setToSkuId(goods.getSkuId());
                    invTrans.setToLocId(null);
                    invTrans.setToWhsId(goods.getWhsId());
                    invTrans.setToQty(goods.getQty());
                    // 更新库存
                    inventoryService.inventoryTransfer(inputLocDtoSub, inputLocDtoAdd, invTrans);
                }

                //更新出库明细状态
                for (CancelPickResponse cancal : cancelPickResponses) {
                    BigDecimal shipQty = cancal.getDtlShipQty();
                    BigDecimal orderQty = cancal.getDtlOrderQty();
                    BigDecimal pickQty = cancal.getDtlPickQty().subtract(cancal.getGoodsSnQty());
                    if (cancal.getDtlPickQty().compareTo(cancal.getGoodsSnQty()) == -1) {
                        Shift.fatal("取消SN数量不能大于当前发货明细的拣货数!");
                    }
                    if (!(headerStatus.equals(GdnHeaderStatusEnum.CLOSE.code)||headerStatus.equals(GdnHeaderStatusEnum.FINISH.code))){
                        Integer updateStatus=null;
                        if (pickQty.compareTo(BigDecimal.ZERO) == 0){
                            updateStatus = GdnDtlStatusEnum.CREATE.code;
                        }
                        if (shipQty.compareTo(BigDecimal.ZERO) == 0) {
                            if (pickQty.compareTo(orderQty) == 0) {
                                updateStatus = GdnDtlStatusEnum.WAITLOAD.code;
                            } else if (pickQty.compareTo(BigDecimal.ZERO) == 0) {
                                updateStatus = GdnDtlStatusEnum.CREATE.code;
                            } else {
                                updateStatus = GdnDtlStatusEnum.PICKING.code;
                            }
                        }
                        if (!flag){
                            updateStatus = GdnDtlStatusEnum.CANCEL.code;
                        }
                        if (updateStatus!=null){
                            pickTaskMapper.updateGdnDtlStatus(cancal.getGdnDtlId(), updateStatus, cancal.getGoodsSnQty());
                        }else {
                            pickTaskMapper.updateGdnDtlQty(cancal.getGdnDtlId(),cancal.getGoodsSnQty());
                        }

                    } else {
                        pickTaskMapper.updateGdnDtlQty(cancal.getGdnDtlId(),cancal.getGoodsSnQty());
                    }
                }

                if (!(headerStatus.equals(GdnHeaderStatusEnum.CLOSE.code)||headerStatus.equals(GdnHeaderStatusEnum.FINISH.code))){
                    // 更新出库主档状态
                    PickAllQty pickAllQty = pickTaskMapper.selectAllQty(gdnHeaderId);
                    Integer updateHeaderStatus;
                    // 待修改
                    if (pickAllQty.getShipQty().compareTo(BigDecimal.ZERO) == 0) {
                        if (pickAllQty.getPickQty().compareTo(pickAllQty.getOrderQty()) == 0) {
                            updateHeaderStatus = GdnHeaderStatusEnum.DOING.code;
                            pickTaskMapper.updateGdnHeaderStatus(gdnHeaderId, updateHeaderStatus);
                        } else {
                            updateHeaderStatus = GdnHeaderStatusEnum.DOING.code;
                            pickTaskMapper.updateGdnHeaderStatus(gdnHeaderId, updateHeaderStatus);
                        }
                    }
                    if (pickAllQty.getPickQty().compareTo(BigDecimal.ZERO) == 0) {
                        updateHeaderStatus = GdnHeaderStatusEnum.CREATE.code;
                        pickTaskMapper.updateGdnHeaderStatus(gdnHeaderId, updateHeaderStatus);
                    }
                }

                // 删除出库单货品
                List<BigDecimal> dtlGoodsIds = cancelPickResponses.stream().map(t -> t.getGdnDtlGoodsId()).collect(Collectors.toList());
                gdnDtlGoodsMapper.delete(new QueryWrapper<GdnDtlGoods>().in("GDN_DTL_GOODS_ID",dtlGoodsIds));

                //日志
                GoodsLog goodsLog = new GoodsLog();
                Goods goodsUp = goodsMapper.selectBySn(sn);
                goodsUp.setLocId(null);
                goodsLog.setGoodsSn(sn);
                goodsLog.setComments(GoodsLogEnum.RAW_UN_PICK.getEnumName());
                goodsLog.setReason(GoodsLogEnum.RAW_UN_PICK.getEnumName());
                goodsLog.setOpCode(GoodsLogEnum.RAW_UN_PICK.getCode());
                goodsLog.setOperator(loginName);
                goodsLog.setCreateTime(date);
                goodsLog.setCreateUser(loginName);
                goodsLog.setUpdateContent("未更新:" + JSON.toJSONString(goods) + ",更新后:" + JSON.toJSONString(goodsUp));
                goodsLogMapper.insert(goodsLog);
            }
            pickTaskMapper.updateGoodsLoc(goodsIds);

            // 调用SAP接口【取消拣货--->单子的所有条码都清除完毕的时候调用】
            if (flag){
                GdnHeader gdnHeader = gdnHeaderMapper.selectById(list.get(0).getGdnHeaderId());
                int goodsSnSize = gdnDtlGoodsMapper.selectCountSnGdn(gdnHeader.getGdnHeaderId());
                if (goodsSnSize == 0) {
                    try {
                        ZDT_076_ReqItems record = new ZDT_076_ReqItems();
                        record.setBILLTYPE("1"); 			// 默认1
                        record.setBILLNO(gdnHeader.getGdnNo());
                        record.setZDATE(DateUtil.format(date, "yyyyMMdd"));
                        record.setZTIME(DateUtil.format(date, "HHmmss"));
                        record.setSTATUS("2"); 				// 1确认、2取消
                        record.setZNAME(loginName);
                        wmsBillStatusBackService.backToSap(record);
                    } catch (SystemException e) {
                        Shift.fatal(e.getMessage());
                    } catch (Exception e) {
                        Shift.fatal("调用SAP接口失败");
                    }
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            rLock.unlock();
        }
//		Shift.fatal("/////////////////");
        return new Result<>();
    }

    /**
     * 拣料拆分
     *
     * @param gdnHeaderId
     * @param skuId
     * @param sn
     * @param whsId
     * @param splitcount
     * @param servletRequest
     * @param servletResponse
     * @return
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public Result<ScanResponse> split(Integer gdnHeaderId, Integer skuId, String sn, Integer whsId, Integer splitcount, HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws Exception {

        try {
            lock.lock();

            Goods goods = goodsMapper.selectBySn(sn);
            // 【检验状态】待入库/已拣选/已出库/在途/已报废
            if(Objects.equals(goods.getStatus(), GoodsStatusEnum.WAIT_IN.code) ||
                    Objects.equals(goods.getStatus(), GoodsStatusEnum.HAS_DOWN.code) ||
                    Objects.equals(goods.getStatus(), GoodsStatusEnum.HAS_CHUKU.code) ||
                    Objects.equals(goods.getStatus(), GoodsStatusEnum.OUT_ONLOAD.code) ||
                    Objects.equals(goods.getStatus(), GoodsStatusEnum.SCRAP.code)) {
                Shift.fatal(String.format("SN[%s]状态为[%s]不可进行拆分", goods.getGoodsSn(), GoodsStatusEnum.getEnumByCode(goods.getStatus()).enumName));
            }

            BigDecimal splitQty = new BigDecimal(splitcount);
            if (goods.getQty().compareTo(splitQty) == -1) {
                Shift.fatal(StatusCode.OPERATE_ERR, "拆分的数量不能大于该物料的总数");
            }

            // 操作人、当前系统时间
            String loginName = HttpSessionUtil.getLoginName(servletRequest);
            Date date = new Date();

            // 新条码SN
            String newGoodsSn = goodsSerialCoreService.generateGoodsSn(goods.getGoodsSn());

            // 新条码
            Goods goodsNew = new Goods();
            BeanUtils.copyProperties(goods, goodsNew);
            goodsNew.setGoodsId(null);
            goodsNew.setPkgId(null);
            goodsNew.setModifyTime(null);
            goodsNew.setModifyUser(null);

            goodsNew.setCreateTime(date);
            goodsNew.setCreateUser(loginName);
            goodsNew.setGoodsSn(newGoodsSn);

            goodsNew.setQty(splitQty);
            goodsNew.setIsOriginalPackage(0);
            goodsNew.setSourceGoodsId(goods.getGoodsId());
            goodsNew.setComments(String.format("拆分自[%s]，拆分数量[%s]", goods.getGoodsSn(), splitQty.toString()));
            goodsMapper.insert(goodsNew);

            // 新条码LOG
            GoodsLog goodsLog = new GoodsLog();
            goodsLog.setGoodsSn(goodsNew.getGoodsSn());
            goodsLog.setOperator(loginName);
            goodsLog.setCreateUser(loginName);
            goodsLog.setCreateTime(date);
            goodsLog.setOpCode(GoodsLogEnum.CREATE.getCode());
            goodsLog.setReason(String.format("源SN[%s]拆分建档", goods.getGoodsSn()));
            goodsLog.setUpdateContent(JSON.toJSONString(goodsNew));
            goodsLogMapper.insert(goodsLog);

            // 原条码
            String jsonStringBefore = JSON.toJSONString(goods);
            goodsMapper.splitQty(goods.getGoodsId(), splitQty.negate(), date, loginName);
            String jsonStringUpdate = JSON.toJSONString(goodsMapper.selectBySn(sn));

            // 原条码LOG
            GoodsLog goodsLogSource = new GoodsLog();
            goodsLogSource.setGoodsSn(goods.getGoodsSn());
            goodsLogSource.setOperator(loginName);
            goodsLogSource.setCreateUser(loginName);
            goodsLogSource.setCreateTime(date);
            goodsLogSource.setOpCode(GoodsLogEnum.RAW_SPLIT.getCode());
            goodsLogSource.setReason(GoodsLogEnum.RAW_SPLIT.getEnumName());
            goodsLogSource.setUpdateContent("未更新:" + jsonStringBefore + ",更新后:" + jsonStringUpdate);
            goodsLogMapper.insert(goodsLogSource);

            // 查询当前物料出库单明细
            QueryWrapper<GdnDtl> wrapper = new QueryWrapper<>();
            wrapper.eq("GDN_HEADER_ID", gdnHeaderId);
            wrapper.eq("SKU_ID", goods.getSkuId());
            wrapper.last("ORDER BY GDN_LINE_NO ASC");
            List<GdnDtl> gdnDtlList = gdnDtlMapper.selectList(wrapper);

            // 调用【拣料服务】
            GdnHeader gdnHeader = gdnHeaderMapper.selectById(gdnHeaderId);
            pick(goodsNew, gdnDtlList, loginName, date,gdnHeader);

            // 响应实体
            ScanResponse response = new ScanResponse();
            response.setGoodsId(goodsNew.getGoodsId());
            response.setSn(goodsNew.getGoodsSn());
            response.setCount(splitQty);
            return new Result<ScanResponse>(response);
        }catch (Exception e){
            e.printStackTrace();
            throw e;
        }finally {
            lock.unlock();
        }
    }

    /**
     * 扫码获取推荐库位
     *
     * @param whsId
     * @param skuId
     * @param gdnHeaderId
     * @param locCode
     * @param servletRequest
     * @param servletResponse
     * @return
     */
    public Result<LocResponse> getLocByCode(Integer whsId, Integer skuId, Integer gdnHeaderId, String locCode, HttpServletRequest servletRequest, HttpServletResponse servletResponse) {


        Result<LocResponse> result = new Result<>();

        Location location = locationMapper.selectOneByLocCode(locCode);
        if (location==null){
            Shift.fatal(StatusCode.OPERATE_ERR, "未找到相关库位信息");
        }
        List<LocResponse> locResponseList = goodsMapper.selectAllLocIdBySkuId(skuId, location.getLocId());
        if (locResponseList.size() == 0) {
            Shift.fatal(StatusCode.OPERATE_ERR, "未找到相关库位信息");
        }
        //推荐库位整合
        this.getRecommendLoc(whsId, skuId, gdnHeaderId, locResponseList);
        if (!CollectionUtil.isEmpty(locResponseList)){
            locResponseList=locResponseList.stream().filter(t ->
                    t.getTotalQty()!=null).collect(Collectors.toList());
            locResponseList=locResponseList.stream().filter(t ->
                    t.getTotalQty().compareTo(BigDecimal.ZERO)==1).collect(Collectors.toList());
        }

        result.setData(locResponseList.get(0));
        return result;
    }

    /**判断对应订单是否能直接提交拣料
        * @param gdnHeaderId
        * @return com.wzdigit.wms.basic.client.response.Result
        * @author shuaic
        * @date 2021/12/1 11:17
    */





    @Reference(version = "${dubbo.providers.basic.BasicParam.api:1.0.0}", check = false, timeout = 300000)
    BasicParamApi basicParamApi;

    public Boolean getBasicParam(String  paramCode){
        ParamDetailDTO jit_isCheckComplete = basicParamApi.getByCode(paramCode);
        if (jit_isCheckComplete!=null && jit_isCheckComplete.getSystemParamValue()!=null){
            String systemParamValue = jit_isCheckComplete.getSystemParamValue();
            if (systemParamValue.equals("true")){
                return true;
            }else {
                return false;
            }
        }
        return true;
    }

    public Result<?> checkPermission(BigDecimal gdnHeaderId) {
        return  deliveryService.checkPermission(gdnHeaderId);
    }
}
